## Du test à la preuve : ### Introduction au Property Based Testing
  • Une personne de plus de 18 ans est un adulte
  • Une personne émancipé est un adulte
  • Un adulte peut obtenir le permis
```php final class Person { public function emancipate(): void; public function isAdult(Clock $clock): bool; public function age(Clock $clock): int; public function obtainDriverLicense( Clock $clock, DeliverDriverLicense $deliver ): void; public function hasDriverLicense(): bool; } ```
```php final class DeliverDriverLicense { public function __invoke( string $firstName, string $lastName, PointInTime $birthday, string $placeOfBirth ): DriverLicense; } ``` Note: On est dans un projet réel Un collègue l'a déjà implémenté donc on n'a même pas à s'en préoccuper

Tests traditionnels

public function testIsAdult()
{
    $clock = new FrozenClock(new PointInTime('2020-05-15 11:30:00'));

    $person = new Person(
        'John',
        'Doe',
        $clock->at('2000-05-16 03:00:00'),
        'Somewhere',
    );
    $this->assertTrue($person->isAdult($clock));
}
                        
public function testIsNotAndAdult()
{
    $clock = new FrozenClock(new PointInTime('2020-05-15 11:30:00'));

    $person = new Person(
        'John',
        'Doe',
        $clock->at('2004-05-14 03:00:00'),
        'Somewhere',
    );
    $this->assertFalse($person->isAdult($clock));
}
                        
```php public function testAgeBeforeBirthday(); public function testAgeAfterBirthday(); public function testEmancipatedCitizenIsConsideredAnAdult(); public function testAnAdultCanObtainADriverLicense(); public function testANonAdultCantObtainADriverLicense(); ```
![](pictures/traditional.png) Note: 3 règles couvertes 100% coverage

C'est bon ça fonctionne...

...pour John Doe

Propery Based Testing

### Un peu d'historique Note: Vient de la prog fonctionnelle Née en 1999 avec QuickCheck (Haskell) par John Hughes
### Les principes Note: Type system On décrit un comportement attendu Le framework va chercher un moyen d'invalider ce comportement

PBT appliqué à PHPUnit

```sh composer require --dev innmind/black-box ```
use Innmind\BlackBox\{
    PHPUnit\BlackBox,
    Set,
};

class Test extends \PHPUnit\Framework\TestCase
{
    use BlackBox;

    public function testProperty()
    {
        $this->forAll(/* inputs */)
            ->then(function(...$inputs) {
                // assertions
            });
    }
}
                        
public function testCitizenIsConsideredAnAdultWhenHeReachesHis18thBirthday()
{
    $this
        ->forAll(
            PointInTime::before('3000-01-01T00:00:00'),
            $this->ageBetween(18, 130), // small chance someone will be older than that
            Set\Strings::any(),
            Set\Strings::any(),
            Set\Strings::any(),
        )
        ->then(function($birthday, $age, $firstName, $lastName, $placeOfBirth) {
            $now = $birthday->goForward($age);
            $clock = new FrozenClock($now);
            $person = new Person(
                $firstName,
                $lastName,
                $birthday,
                $placeOfBirth,
            );

            $this->assertTrue($person->isAdult($clock));
        });
}
                        
# PersonSet.php
public static function over18YearsOld(): Set
{
    return Set\Composite::mutable(
        function($firstname, $lastname, $birthday, $age, $placeOfBirth) {
            return [
                new Person($firstname, $lastname, $birthday, $placeOfBirth),
                new FrozenClock($birthday->goForward($age)),
            ];
        },
        Set\Strings::any(),
        Set\Strings::any(),
        PointInTime::before('3000-01-01T00:00:00'),
        self::ageAbove(18),
        Set\Strings::any(),
    );
}
                        
# PersonSet.php
public static function anyAdult(): Set
{
    return new Set\Either(
        self::over18YearsOld(),
        self::emancipated(),
    );
}
                        
public function testAnyAdultCanObtainADriverLicense()
{
    $this
        ->forAll(PersonSet::anyAdult())
        ->then(function($personAndClock) {
            [$person, $clock] = $personAndClock;
            $deliver = new DeliverDriverLicense($clock);

            $this->assertFalse($person->hasDriverLicense());
            $person->obtainDriverLicense($clock, $deliver);
            $this->assertTrue($person->hasDriverLicense());
        });
}
                        
![](pictures/pbt.png)
## En résumé Note: Des tests vraiment exhaustifs Les tests deviennent une documentation vivante Renforce la méfiance sur les tests verts
# Questions Twitter @Baptouuuu Github @Baptouuuu/talks